package org.finesys.common.tenant.config;

import java.util.Objects;

import org.finesys.common.mp.core.utils.MyBatisUtils;
import org.finesys.common.tenant.aspect.IgnoreTenantAspect;
import org.finesys.common.tenant.cache.TenantRedisCacheManager;
import org.finesys.common.tenant.filter.TenantContextWebFilter;
import org.finesys.common.tenant.filter.TenantSecurityWebFilter;
import org.finesys.common.tenant.interceptor.FeignTenantInterceptor;
import org.finesys.common.tenant.mybatis.TenantDatabaseHandler;
import org.finesys.common.tenant.properties.TenantProperties;
import org.finesys.common.tenant.service.TenantValidService;
import org.finesys.system.api.service.TenantService;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.security.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.TenantLineInnerInterceptor;

import feign.RequestInterceptor;

@AutoConfiguration
@ConditionalOnProperty(prefix = "tenant", value = "enable", matchIfMissing = true)
@EnableConfigurationProperties(TenantProperties.class)
public class TenantAutoConfiguration {

    /**
     * Mybytis-plus多租户拦截器
     *
     * @param tenantProperties       多租户配置
     * @param mybatisPlusInterceptor Mybatis-plus拦截器
     * @return Mybatis-plus多租户拦截器
     */
    @Bean
    public TenantLineInnerInterceptor tenantLineInnerInterceptor(TenantProperties tenantProperties, MybatisPlusInterceptor mybatisPlusInterceptor) {
        // Mybatis-plus多租户拦截器
        TenantLineInnerInterceptor innerInterceptor = new TenantLineInnerInterceptor(new TenantDatabaseHandler(tenantProperties));
        // 添加拦截器到首个位置 保证在分页插件前执行
        MyBatisUtils.addInterceptor(mybatisPlusInterceptor, innerInterceptor, 0);
        return innerInterceptor;
    }

    /**
     * 多租户上下文过滤器
     */
    @Bean
    public FilterRegistrationBean<TenantContextWebFilter> tenantContextWebFilter() {
        FilterRegistrationBean<TenantContextWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TenantContextWebFilter());
        registrationBean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER - 1);
        return registrationBean;
    }


    @Bean
    public TenantValidService tenantValidService(TenantService tenantService) {
        return new TenantValidService(tenantService);
    }

    /**
     * 租户合法校验过滤器
     */
    @Bean
    public FilterRegistrationBean<TenantSecurityWebFilter> tenantSecurityWebFilter(TenantProperties tenantProperties, TenantValidService tenantValidService) {
        FilterRegistrationBean<TenantSecurityWebFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new TenantSecurityWebFilter(tenantProperties, tenantValidService));
        registrationBean.setOrder(SecurityProperties.DEFAULT_FILTER_ORDER + 1);
        return registrationBean;
    }

    /**
     * 传递Feign租户请求头
     */
    @Bean
    @ConditionalOnProperty(prefix = "feign", name = "enabled", havingValue = "true")
    public RequestInterceptor feignRequestInterceptor() {
        return new FeignTenantInterceptor();
    }

    /**
     * 多级缓存不开启时生效
     */
    @ConditionalOnProperty(prefix = "redis.cache.multi", name = "enabled", havingValue = "false", matchIfMissing = false)
    @Bean
    @Primary
    public RedisCacheManager redisCacheManager(RedisTemplate<String,Object> redisTemplate) {
        RedisConnectionFactory redisConnectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig().
                serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()));
        return new TenantRedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }

    /**
     * 多租户aop处理
     */
    @Bean
    public IgnoreTenantAspect ignoreTenantAspect() {
        return new IgnoreTenantAspect();
    }
}
